package com.kokomi.controller;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.kokomi.handler.Assert;
import com.kokomi.pojo.DTO.LoginRequestDTO;
import com.kokomi.pojo.DTO.RegisterRequestDTO;
import com.kokomi.pojo.RedisConst;
import com.kokomi.pojo.User;
import com.kokomi.pojo.VO.UserVo;
import com.kokomi.pojo.enumeration.BusinessCode;
import com.kokomi.service.UserService;
import com.kokomi.util.JwtUtil;
import com.kokomi.util.ProductionCaptchaUtil;
import com.kokomi.util.SmsUtil;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {

    @Autowired
    private UserService userService;

    @Autowired
    private JwtUtil jwtUtil;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private SmsUtil smsUtil;

    @Autowired
    private JavaMailSender mailSender; // 注入邮件发送器

    //发件人
    @Value("${spring.mail.username}")
    private String from;

    @Autowired
    private RabbitTemplate rabbitTemplate;


    /**
     * 登入
     * @param loginRequestDTO
     * @return
     */
    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequestDTO loginRequestDTO, HttpServletRequest request) {
        // 1.调用业务层进行登入
        ResponseEntity<?> responseEntity = userService.login(loginRequestDTO);

        // 2.如果登入成功则创建token
        if (responseEntity.getStatusCode() == HttpStatus.OK) {
            // 得到用户对象
            User user = (User) responseEntity.getBody();
            // 登入成功，创建token
            return createTokenAndUserVo(user, request);
        } else {
            // 返回登入失败
            return responseEntity;
        }

    }

    /**
     * 生成Token，并且封装UserVo对象
     */
    private ResponseEntity<?> createTokenAndUserVo(User user, HttpServletRequest request) {
        // 1.生成token
        HashMap<String, Object> data = new HashMap<>();
        data.put("id", user.getId());
        data.put("userName", user.getAccount());

        // 为了安全，判断ip地址是否相同
        data.put("ip", request.getRemoteAddr());
        String token = jwtUtil.createToken(data);
        log.info("用户ID：{} 登录成功，生成的Token为：{}", user.getId(), token);

        // 储存到redis中保存1天
        String key = RedisConst.USER_TOKEN_PREFIX + ":" + user.getId();
        stringRedisTemplate.opsForValue().set(key, token, Duration.ofDays(1));

        // 封装UserVo对象
        UserVo userVo = new UserVo();

        // 复制属性：源对象，目标对象
        BeanUtils.copyProperties(user, userVo);

        // 以响应头的方式发送Token回去
        return ResponseEntity.ok()
                .header("Authorization", token)
                //告知浏览器可以访问 Authorization 响应头，否则访问不了
                .header("Access-Control-Expose-Headers", "Authorization")
                .body(userVo);
    }


    /**
     * 获取验证码
     * @return
     */
    @GetMapping("code")
    public ResponseEntity getCode() throws IOException {
        //(2) 创建缓存图片：指定长、宽
        int width = 110, height = 45;

        //参数3：图片的类型，使用RGB类型
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        //(3) 获取画笔对象
        Graphics graphics = image.getGraphics();

        //(4) 设置画笔颜色，并且填充矩形区域
        graphics.setColor(Color.WHITE);
        graphics.fillRect(0, 0, width, height);

        //创建一个StringBuider对象
        StringBuilder sb = new StringBuilder();

        //(5) 从字符数组中随机得到字符
        String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        char[] arr = characters.toCharArray();

        Random random = new Random();
        for (int i = 0; i < 4; i++) {
            //从字符数组中随机得到字符
            char c = arr[random.nextInt(arr.length)];
            //将字符添加到StringBuilder对象中
            sb.append(c);
            //设置字体，大小为22，设置字的颜色随机
            graphics.setFont(new Font(null, Font.BOLD + Font.ITALIC, 22));
            graphics.setColor(getColor());
            //将字符转成字符串
            graphics.drawString(c + "", i * 25 + 10, 25);
        }

        //(6) 画干扰线8条线，线的位置是随机的，x范围在width之中，y的范围在height之中。
        for (int i = 0; i < 8; i++) {
            graphics.setColor(getColor());
            int x1 = random.nextInt(width);
            int y1 = random.nextInt(height);
            int x2 = random.nextInt(width);
            int y2 = random.nextInt(height);
            graphics.drawLine(x1, y1, x2, y2);
        }
        //(7) 将缓存的图片输出到响应输出流中
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        //参数1：要输出的缓存图片，参数2：格式，参数3：输出到哪个流中
        ImageIO.write(image, "jpeg", bos);

        //随机生成一个key，前面加上code::
        String key = RedisConst.USER_VALIDATE_CODE + ":" + UUID.randomUUID().toString().replace("-", "");

        //得到验证码，将生成的字符串放到Redis中，键随机生成，10分钟过期
        stringRedisTemplate.opsForValue().set(key, sb.toString(), Duration.ofMinutes(1));
        log.info("生成的验证码为：{}，Redis中的key是：{}", sb, key);
        Base64.Encoder encoder = Base64.getEncoder();
        String base64Img = "data:image/jpeg;base64," + encoder.encodeToString(bos.toByteArray());

        //返回的数据包含验证码的key和图片
        //返回的数据包含验证码的key和图片
        Map<String, String> data = new HashMap<>();
        data.put("key", key);  //redis中键
        data.put("base64Img", base64Img);  //图片
        //返回key和图片的base64的编码
        return ResponseEntity.ok(data);
    }

    //写一个方法随机获取颜色
    private Color getColor() {
        Random random = new Random();
        //红，绿，蓝取值0-255，随机生成 0-255之间数
        int r = random.nextInt(256);
        int g = random.nextInt(256);
        int b = random.nextInt(256);
        return new Color(r, g, b);
    }


    /**
     * 获取手机验证码
     * @param loginRequestDTO
     * @return
     */
    @PostMapping("/getTelCode")
    public ResponseEntity getTelCode(@RequestBody LoginRequestDTO loginRequestDTO) {
        String smsCode = ProductionCaptchaUtil.creatCode();
//        String response = smsUtil.sendSms(loginRequestDTO.getTelephone(), smsCode+" ");
        String response = "OK";
        System.out.println("=====================" + smsCode);
        Assert.error(!response.equals("OK"), BusinessCode.SMS_SEND_ERROR);
        // 将验证码保存到redis中
        stringRedisTemplate.opsForValue().set(loginRequestDTO.getTelephone(), smsCode, Duration.ofMinutes(10));
        return ResponseEntity.ok(BusinessCode.SMS_SEND_SUCCESS);
    }

    /**
     * 退出
     * 同时清除redis中的token
     */
    @DeleteMapping("/logout/{id}")
    public ResponseEntity logout(@PathVariable("id") Long id) {

        String key = RedisConst.USER_TOKEN_PREFIX + ":" + id;
        Boolean delete = stringRedisTemplate.delete(key);
        Assert.error(!delete, BusinessCode.USER_NOT_LOGGED);


        return ResponseEntity.ok(BusinessCode.LOGOUT_SUCCESS);
    }

    /**
     * 手机号登录
     * @param loginRequestDTO
     * @return
     */
    @PostMapping("/TelLogin")
    public ResponseEntity<?> TelLogin(@RequestBody LoginRequestDTO loginRequestDTO) {

        // 1.将手机号获取
        String telephone = loginRequestDTO.getTelephone();

        // 2。通过手机号获取redis中验证码//2。通过手机号获取redis中验证码
        String redisCode = stringRedisTemplate.opsForValue().get(telephone);

        //3.判断验证码是否存在(过期)
        Assert.error(redisCode == null, BusinessCode.INVALID_VERIFICATION);

        //4.判断验证码是否输入正确
        Assert.error(!redisCode.equals(loginRequestDTO.getSmsCode()), BusinessCode.VERIFICATION_ERROR);

        //5.判断手机号是否存在
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getMobile, telephone);
        User one = userService.getOne(queryWrapper);
        if (one != null) {
            // 6.判断用户状态是否正常
            Assert.error(one.getState() != 1, BusinessCode.USER_FROZEN_ERROR);
            // 如果存在，则返回用户信息
            return ResponseEntity.status(BusinessCode.LOGIN_SUCCESS.getCode()).body(one);
        } else {
            //7.如果不存在，则默认注册一个账号
            String password = BCrypt.hashpw("123", BCrypt.gensalt());
            User user = new User();
            user.setAccount("游客" + telephone);
            user.setPassword(password);
            user.setMobile(telephone);
            user.setState(1);
            userService.save(user);
            // 则返回用户信息
            return ResponseEntity.status(BusinessCode.LOGIN_SUCCESS.getCode()).body(user);
        }
    }

    /**
     * 注册
     * @param registerRequestDTO
     * @return
     */
    @PostMapping("/register")
    public ResponseEntity<?> register(@RequestBody RegisterRequestDTO registerRequestDTO) {
        return userService.register(registerRequestDTO);

    }

    /**
     * 邮箱验证码
     * @param Email
     * @return
     */
    @GetMapping("/getEmailCode/{Email}")
    public ResponseEntity getEmailCode(@PathVariable("Email") String Email) {
        // 1.判断邮箱是否存在
        Assert.error(userService.isEmailExist(Email), BusinessCode.EMAIL_EXIST_ERROR);
        // 2.编辑邮件
        SimpleMailMessage mail = new SimpleMailMessage();
        mail.setSubject("蜗牛书城：验证码");
        String code = ProductionCaptchaUtil.creatCode() + "";
        System.out.println(code);
        mail.setText("您的验证码为：" + code + "有效期为15分钟");
        mail.setFrom(from);
        mail.setTo(Email);
        // 3.发送
        new Thread(() -> {
//            mailSender.send(mail);
        }).start();
        // 4.将验证码保存到redis中
        stringRedisTemplate.opsForValue().set(RedisConst.EMAIL_ADDRESS_PREFIX + ":" + Email, code + "", Duration.ofMinutes(15));
        // 5.返回结果

        return ResponseEntity.ok(BusinessCode.SMS_SEND_SUCCESS);
    }

    @GetMapping("/send")
    public ResponseEntity<String> sendEmail(String address) {
        // 1.生成4位验证码
        String code = ProductionCaptchaUtil.creatCode();
        String context = "您的验证码为：" + code + "有效期为15分钟";

        // 2.将消息封装到map中，给MP
        Map<String, String> map = new HashMap<>();
        map.put("from", from);
        map.put("to", address);
        map.put("subject", "蜗牛书城：验证码");
        map.put("context", context);

        // 发送消息到RabbitMQ队列中 参数1：队列名称 参数2：消息内容
        rabbitTemplate.convertAndSend("email.queue", map);

        //将数据存储在Redis中,时间设置为15分钟，邮箱地址是键
        stringRedisTemplate.opsForValue().set(RedisConst.EMAIL_ADDRESS_PREFIX + ":" + address,
                code,
                RedisConst.EMAIL_CODE_EXPIRE,
                TimeUnit.MINUTES);

        return ResponseEntity.ok("验证码已经发送到" + address + "邮箱，请去查收！");
    }

}
